﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RegionHelper.cs" company="http://buginbox.wordpress.com/">
//   Copyright (c) GNU General Public License version 2 (GPLv2)
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.ComponentModel;
using System.Windows;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.ServiceLocation;
using Wpf.Core.Behavior;
using Wpf.Core.Control;
using Wpf.Core.ViewModel;

namespace Wpf.Core.Region
{
    /// <summary>
    /// Region Helper
    /// </summary>
    public static class RegionHelper
    {
        #region Private members

        /// <summary>
        /// The _activated window.
        /// </summary>
        private static Window _activatedWindow;

        #endregion Private members

        /// <summary>
        /// Adds the view.
        /// </summary>
        /// <typeparam name="TView">
        /// The type of the view. 
        /// </typeparam>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        /// <param name="regionIndex">
        /// Index of the region. 
        /// </param>
        /// <param name="view">
        /// The view. 
        /// </param>
        /// <param name="viewName">
        /// The view id. 
        /// </param>
        /// <param name="viewModel">
        /// The view model. 
        /// </param>
        /// <returns>
        /// Returns the new RegionManager if no view already exists in the region with the same viewId Otherwise returns null 
        /// </returns>
        public static IRegionManager AddView<TView>(string regionName, int regionIndex, TView view, string viewName, ViewModelBase viewModel)
            where TView : System.Windows.Controls.Control, IControlBase
        {
            var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
            return AddView(regionManager, regionName, regionIndex, view, viewName, viewModel);
        }

        /// <summary>
        /// Compares the views.
        /// </summary>
        /// <param name="view1">
        /// The view1. 
        /// </param>
        /// <param name="view2">
        /// The view2. 
        /// </param>
        /// <returns>
        /// The compare views. 
        /// </returns>
        private static int CompareViews(object view1, object view2)
        {
            return string.Compare(((IControlBase)view1).Order.ToString(), ((IControlBase)view2).Order.ToString());
        }

        /// <summary>
        /// Compares the views.
        /// </summary>
        /// <param name="viewModel1">
        /// The view Model 1. 
        /// </param>
        /// <param name="viewModel2">
        /// The view Model 2. 
        /// </param>
        /// <returns>
        /// The compare view models. 
        /// </returns>
        private static int CompareViewModels(object viewModel1, object viewModel2)
        {
            return string.Compare(((ViewModelBase)viewModel1).Order.ToString(), ((ViewModelBase)viewModel2).Order.ToString());
        }

        /// <summary>
        /// Adds the view.
        /// </summary>
        /// <typeparam name="TView">
        /// The type of the view. 
        /// </typeparam>
        /// <param name="regionManager">
        /// The region manager. 
        /// </param>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        /// <param name="regionIndex">
        /// Index of the region. 
        /// </param>
        /// <param name="view">
        /// The view. 
        /// </param>
        /// <param name="viewName">
        /// The view id. 
        /// </param>
        /// <param name="viewModel">
        /// The view model. 
        /// </param>
        /// <returns>
        /// Returns the new RegionManager if no view already exists in the region with the same viewId Otherwise returns null 
        /// </returns>
        public static IRegionManager AddView<TView>(IRegionManager regionManager, string regionName, int regionIndex, TView view, string viewName, ViewModelBase viewModel)
            where TView : System.Windows.Controls.Control, IControlBase
        {
            // Check input parameters
            if (regionManager == null || view == null || string.IsNullOrEmpty(regionName) || string.IsNullOrEmpty(viewName) || regionIndex < 0)
            {
                throw new ArgumentException("[RegionHelper.AddView] Invalid parameter(s)");
            }

            if (!regionManager.Regions.ContainsRegionWithName(regionName))
            {
                throw new ArgumentException(string.Format("[RegionHelper.AddView] Region name='{0}' not found.", regionName));
            }

            // Get the view from within the view will be inserted
            IRegion region = regionManager.Regions[regionName];

            // Define a specific comparer to order by Order property
            region.SortComparison = CompareViews;

            // Check if the view already exists in the region
            var existingView = region.GetView(viewName) as TView;

            if (existingView != null)
            {
                region.Activate(existingView);
                return null;
            }

            // Add the view in the region
            // Create a new instance of regionManager (true parameter), to allow sub-view with same name
            view.DataContext = viewModel;
            view.Order = regionIndex;
            IRegionManager returnedRegionManager = region.Add(view, viewName, true);

            // Activate the view
            region.Activate(view);

            return returnedRegionManager;
        }

        /// <summary>
        /// Activates the specified region manager.
        /// </summary>
        /// <param name="regionManager">
        /// The region manager. 
        /// </param>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        /// <param name="viewName">
        /// Name of the view. 
        /// </param>
        /// <param name="viewModel">
        /// The view model. 
        /// </param>
        public static void ActivateView(IRegionManager regionManager, string regionName, string viewName, ViewModelBase viewModel)
        {
            // Check input parameters
            if (regionManager == null || string.IsNullOrEmpty(regionName) || string.IsNullOrEmpty(viewName))
            {
                throw new ArgumentException("[RegionHelper.AddView] Invalid parameter(s)");
            }

            if (!regionManager.Regions.ContainsRegionWithName(regionName))
            {
                throw new ArgumentException(string.Format("[RegionHelper.AddView] Region name='{0}' not found.", regionName));
            }

            // Get the view from within the view will be inserted
            IRegion region = regionManager.Regions[regionName];

            // Force view desactivation (region.ActiveView is not updated if tab is changed by user)
            // Activate so always show view "viewModel"
            region.Deactivate(viewModel);

            region.Activate(viewModel);
        }

        /// <summary>
        /// Shows the popup view.
        /// </summary>
        /// <typeparam name="TView">
        /// The type of the view. 
        /// </typeparam>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        /// <param name="view">
        /// The view. 
        /// </param>
        /// <param name="viewModel">
        /// The view model. 
        /// </param>
        public static void ShowPopupView<TView>(string regionName, TView view, ViewModelBase viewModel) where TView : System.Windows.Controls.Control, IControlBase
        {
            // Check input parameters
            if (view == null || string.IsNullOrEmpty(regionName))
            {
                throw new ArgumentException("[RegionHelper.ShowPopupView] Invalid parameter(s)");
            }

            var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

            if (!regionManager.Regions.ContainsRegionWithName(regionName))
            {
                throw new ArgumentException(string.Format("[RegionHelper.ShowPopupView] Region name='{0}' not found.", regionName));
            }

            IRegion region = regionManager.Regions[regionName];

            view.DataContext = viewModel;

            view.Loaded += ViewLoaded;

            region.Add(view);
            region.Activate(view); // = ShowDialog()
            region.Remove(view); // Remove the view when the dialog is closed

            view.Loaded -= ViewLoaded;
        }

        /// <summary>
        /// Clear all view in specified region
        /// </summary>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        public static void ClearRegion(string regionName)
        {
            var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

            // Get the viregionew from within the view will be inserted
            IRegion region = regionManager.Regions[regionName];

            foreach (var view in region.Views)
            {
                region.Remove(view);
            }
        }

        /// <summary>
        /// Clears the region.
        /// </summary>
        /// <param name="regionManager">
        /// The region manager. 
        /// </param>
        /// <param name="regionName">
        /// Name of the region. 
        /// </param>
        public static void ClearRegion(IRegionManager regionManager, string regionName)
        {
            IRegion region = regionManager.Regions[regionName];

            foreach (var view in region.Views)
            {
                region.Remove(view);
            }
        }

        /// <summary>
        /// Gets the parent window of the view window
        /// </summary>
        /// <param name="control">
        /// The control. 
        /// </param>
        /// <returns>
        /// The System.Windows.Window.
        /// </returns>
        public static Window GetWindow(System.Windows.Controls.Control control)
        {
            return RegionPopupBehavior.GetOwnerWindow(control);
        }

        #region Events

        /// <summary>
        /// Event raised when the view is loaded
        /// </summary>
        /// <param name="sender">
        /// The sender. 
        /// </param>
        /// <param name="e">
        /// The <see cref="RoutedEventArgs"/> instance containing the event data. 
        /// </param>
        public static void ViewLoaded(object sender, RoutedEventArgs e)
        {
            _activatedWindow = Window.GetWindow((DependencyObject)sender);

            if (_activatedWindow != null)
            {
                _activatedWindow.Activated += WindowActivated;
                _activatedWindow.GotFocus += ActivatedWindowGotFocus;
                _activatedWindow.Closing += ActivatedWindowClosing;
            }
        }

        /// <summary>
        /// Event raised when the window is activated
        /// </summary>
        /// <param name="sender">
        /// The sender. 
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data. 
        /// </param>
        private static void WindowActivated(object sender, EventArgs e)
        {
            var window = (Window)sender;

            if (window.IsActive)
            {
                _activatedWindow = window;
            }
        }

        /// <summary>
        /// Event raised when the window got the focus
        /// </summary>
        /// <param name="sender">
        /// The sender. 
        /// </param>
        /// <param name="e">
        /// The <see cref="RoutedEventArgs"/> instance containing the event data. 
        /// </param>
        private static void ActivatedWindowGotFocus(object sender, RoutedEventArgs e)
        {
            _activatedWindow = (Window)sender;
        }

        /// <summary>
        /// Event raised when the window is closing
        /// </summary>
        /// <param name="sender">
        /// The sender. 
        /// </param>
        /// <param name="e">
        /// The <see cref="CancelEventArgs"/> instance containing the event data. 
        /// </param>
        private static void ActivatedWindowClosing(object sender, CancelEventArgs e)
        {
            var window = (Window)sender;

            if (_activatedWindow != null)
            {
                _activatedWindow.Activated -= WindowActivated;
                _activatedWindow.GotFocus -= ActivatedWindowGotFocus;
                _activatedWindow.Closing -= ActivatedWindowClosing;
            }

            _activatedWindow = window.Owner;
        }

        #endregion Events
    }
}